#include "stdafx.h"

#define VERSION_STRING          _T("1.0.0.1")
#define SLEEP_DIRECTION_UP      1
#define SLEEP_DIRECTION_DOWN    0
#define MAX_COMMENT             2048

#pragma comment (lib, "winmm.lib")

/* Print help info onto default output. */
static void PrintHelp ();
/* Copy given string into spefied buffer. Remove start-up number of characters and other paramters invalid characters. */
static void ReadString ( TCHAR *lpszText, TCHAR *lpszBuffer, size_t dwSkip, int dwAllowedParams );
/* Perform sleeping. */
static void PerformAction ();

/****************************************/
static unsigned long g_dwSeconds                    = 0;
static unsigned char g_bKeyBreak                    = 0;
static unsigned char g_dwDirection                  = SLEEP_DIRECTION_UP;
static TCHAR         g_aComment[MAX_COMMENT + 1]    = _T("");
static TCHAR         g_aDone[MAX_COMMENT + 1]       = _T("");


int _tmain ( int argc, _TCHAR* argv[] )
{
    // check if any requested help info display:
    if ( argc == 1 || ( argc == 2 
        && ( _tcscmp ( argv[1], _T("-help") ) == 0 || _tcscmp ( argv[1], _T("--help") ) == 0 )
                || _tcscmp ( argv[1], _T("/?") ) == 0 ) )
    {
        PrintHelp ();
        return 0;
    }

    // print version info if requested:
    if ( argc == 2 && ( _tcscmp ( argv[1], _T("-v") ) == 0 ) )
    {
        _tprintf ( VERSION_STRING _T("\r\n") );
        return 0;
    }

    if ( !_istdigit ( argv[1][0] ) )
    {
        _tprintf ( _T("Invalid arguments.\r\n") );
        return -1;
    }

    // parse parameters and execute actions:
    g_dwSeconds = _ttol ( argv[1] );

    for ( int i = 2; i < argc; i++ )
    {
        // can break when user pressed key?
        if ( _tcscmp ( argv[i], _T("/k") ) == 0 )
        {
            g_bKeyBreak = 1;
            continue;
        }

        // can count down:
        if ( _tcscmp ( argv[i], _T("/d") ) == 0 )
        {
            g_dwDirection = SLEEP_DIRECTION_DOWN;
            continue;
        }

        // count up:
        if ( _tcscmp ( argv[i], _T("/u") ) == 0 )
        {
            g_dwDirection = SLEEP_DIRECTION_UP;
            continue;
        }

        // comment:
        if ( _tcsncmp ( argv[i], _T("/comment:"), 9 ) == 0 )
        {
            ReadString ( argv[i], g_aComment, 9, 2 );
            continue;
        }

        // when done:
        if ( _tcsncmp ( argv[i], _T("/done:"), 6 ) == 0 )
        {
            ReadString ( argv[i], g_aDone, 6, 0 );
            continue;
        }
    }

    // validate if there is a time to wait:
    if ( g_dwSeconds == 0 )
        return 0;

    PerformAction ();

    return 0;
}

static void PrintHelp ()
{
    _tprintf ( _T("Pretorianie 2008 (C)\r\n") );
    _tprintf ( _T("Sleep v%s\r\n\r\n"), VERSION_STRING );
    _tprintf ( _T(" This utility stops execution of current script for given number of seconds.\r\n\r\n") );
    _tprintf ( _T("Usage:\r\n") );
    _tprintf ( _T("\tsleep.exe nn [/comment:ccc] [/k] [/d | /g] [-v] [-help]\r\n\r\n") );
    _tprintf ( _T("  - nn           - number of seconds to wait\r\n") );
    _tprintf ( _T("  - /comment:ccc - prints given comment (in place of '%%d' it will put the current time left and the next '%%d' for the whole time defined during start-up)\r\n") );
    _tprintf ( _T("  - /k           - keypress will break sleep\r\n") );
    _tprintf ( _T("  - /u           - count up\r\n") );
    _tprintf ( _T("  - /d           - count down\r\n\r\n") );
    _tprintf ( _T("Sample:\r\n") );
    _tprintf ( _T("\tsleep.exe -v    - prints the current version of the program\r\n") );
    _tprintf ( _T("\tsleep.exe -help - prints that info\r\n") );
    _tprintf ( _T("\tsleep.exe 10 /comment:'Waiting %%d to complete...' /k /d - prints the comment and waits 10 seconds\r\n") );
}

static void ReadString ( TCHAR *lpszText, TCHAR *lpszBuffer, size_t dwSkip, int dwAllowedParams )
{
    size_t length = _tcslen ( lpszText );
    int params = 0;

    if ( length > dwSkip )
    {
        if ( lpszText[dwSkip] == _T('"') )
            _tcsncpy_s ( lpszBuffer, MAX_COMMENT, lpszText + dwSkip + 1, length - dwSkip - 2 );
        else
            _tcsncpy_s ( lpszBuffer, MAX_COMMENT, lpszText + dwSkip, length - dwSkip );
    }

    // protect arguments and remove all '%' characters:
    for ( size_t i = 0; i < length; i++ )
    {
        if ( lpszBuffer[i] == _T('%') )
        {
            if ( lpszBuffer[i+1] != _T('d') )
                params++;

            // remove parameter is needed by putting '%' twice:
            if ( params > dwAllowedParams || lpszBuffer[i+1] != _T('d') )
            {
                i++;
                if ( lpszBuffer[i] == _T('\0') )
                    lpszBuffer[i+1] = _T('\0');

                lpszBuffer[i] = _T('%');
            }
        }
    }
}

static void PrintSleepComment ( unsigned long currentSecond )
{
    if ( g_aComment[0] != _T('\0') )
    {
        _tprintf ( _T("\r") );
        _tprintf ( g_aComment, currentSecond, g_dwSeconds );
    }
}

static void PrintDoneComment ()
{
    if ( g_aDone[0] != _T('\0') )
    {
        _tprintf ( _T("\r") );
        _tprintf ( _T("%s"), g_aDone );
    }
}

static void FlushKeyboardInput ()
{
    if ( g_bKeyBreak )
        while ( _kbhit() )
            _getch ();
}

static void PerformAction ()
{
    unsigned long nTime, nEndTime, nNextSec, currentSecond;

    nTime       = timeGetTime ();
    nEndTime    = nTime + 1000 * g_dwSeconds;
    nNextSec    = nTime;

    // remove all key-press from the keyboard buffer:
    FlushKeyboardInput ();

    // calculate the current seconds based on cound direction:
    if ( g_dwDirection == SLEEP_DIRECTION_UP )
            currentSecond = 0;
    else    currentSecond = g_dwSeconds;

    // main loop where wait for given time or keypress if requested:
    while ( nEndTime >= nTime && ( !g_bKeyBreak || !_kbhit() ) )
    {
        nTime = timeGetTime ();

        if ( nNextSec <= nTime )
        {
            PrintSleepComment ( currentSecond );
            nNextSec += 1000;
            if ( g_dwDirection == SLEEP_DIRECTION_UP )
                    currentSecond++;
            else    currentSecond--;
        }
    }

    // remove all key-press from the keyboard buffer:
    FlushKeyboardInput ();

    // print final comment:
    PrintDoneComment ();
}
